package lion.mail;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

import org.apache.commons.lang.StringUtils;

/**
 * 邮件构建器，该类负责构建一个符合规则的可以发送的邮件。
 *
 * @author lion
 * @date 2011-5-26
 * @author lion
 * @data 2013-6-21 把收件人的TO,CC,BCC分开，并可以指定收件人和发件人的名称
 */
public class MailBuilder {

	private List<Attachment> attachmentList = new ArrayList<>();
	private List<Attachment> embedAttachmentList = new ArrayList<>();
	private String subject;
	private InternetAddress from;
	private List<Address> recipients = new ArrayList<>();
	private List<Address> ccRecipients = new ArrayList<>();
	private List<Address> BCCRecipients = new ArrayList<>();
	private Map<String, String> headers = new HashMap<>();

	private String text;
	private String html;

	private MimeMessage message;
	private Session session;

	public void addCCRecipient(String address) {

		this.addCCRecipient(address, null);
	}

	public void addCCRecipient(String address, String person) {

		InternetAddress ia = getAddress(address, person);
		if (ia != null) {
			ccRecipients.add(ia);
		}
	}

	public void addRecipient(String address) {

		this.addRecipient(address, null);
	}

	public void setRecipient(String address) {

		this.setRecipient(address, null);
	}

	public void setRecipient(String address, String person) {

		this.recipients.clear();
		this.addRecipient(address, person);
	}

	public void addRecipient(String address, String person) {

		InternetAddress ia = getAddress(address, person);
		if (ia != null) {
			recipients.add(ia);
		}
	}

	public void addBCCRecipient(String address) {

		this.addBCCRecipient(address, null);
	}

	public void addBCCRecipient(String address, String person) {

		InternetAddress ia = getAddress(address, person);
		if (ia != null) {
			BCCRecipients.add(ia);
		}
	}

	private InternetAddress getAddress(String address, String person) {

		InternetAddress ia;
		try {
			ia = new InternetAddress(address);
		} catch (AddressException e) {
			return null;
		}
		if (person != null) {
			try {
				ia.setPersonal(person, "UTF-8");
			} catch (UnsupportedEncodingException e) {
			}
		}
		return ia;
	}

	public void addAttachment(File file) {

		Attachment attach = new Attachment();
		attach.setName(file.getName());
		attach.attach(file);
		attachmentList.add(attach);
	}

	public void addAttachment(File file, String name) {

		Attachment attach = new Attachment();
		attach.setName(name);
		attach.attach(file);
		attachmentList.add(attach);
	}

	public void addAttachment(byte[] content, String name) {

		Attachment attach = new Attachment();
		attach.setName(name);
		if (content != null) {
			attach.attach(content);
		}
		attachmentList.add(attach);
	}

	public String embedAttachment(File file) {

		Attachment attach = new Attachment();
		attach.setName(file.getName());
		attach.attach(file);
		attach.setCid(Attachment.generateCID());
		embedAttachmentList.add(attach);

		return attach.getCid();
	}

	public String embedAttachment(byte[] content, String name) {

		Attachment attach = new Attachment();
		attach.setName(name);
		if (content != null) {
			attach.attach(content);
		}
		attach.setCid(Attachment.generateCID());
		embedAttachmentList.add(attach);

		return attach.getCid();
	}

	private void buildMessage() throws Exception {

		if (session == null) {
			Properties properties = new Properties();
			session = Session.getInstance(properties);
		}

		message = new MSGIDMessage(session);
		buildMailBody();
	}

	public Message getMessage() throws Exception {

		buildMessage();
		setBasicInfo();

		return message;
	}

	private void buildMailBody() throws MessagingException, IOException {

		Multipart multipart = new MimeMultipart();
		message.setContent(multipart);

		MimeBodyPart htmlBodyPart = null;

		if (html != null) {
			htmlBodyPart = new MimeBodyPart();
			htmlBodyPart.setText(html, "UTF-8", "html");
		}
		MimeBodyPart textBodyPart = null;

		if (text != null) {
			textBodyPart = new MimeBodyPart();
			textBodyPart.setText(text, "UTF-8");
		}

		if (!embedAttachmentList.isEmpty()) {
			buildRelatedBodyPart(multipart, htmlBodyPart, textBodyPart);
		} else {
			if (textBodyPart != null && htmlBodyPart != null) {
				buildAlternativeBodyPart(multipart, htmlBodyPart, textBodyPart);
			} else {
				buildContentPart(multipart, htmlBodyPart, textBodyPart);
			}
		}

		if (!attachmentList.isEmpty()) {
			for (Attachment attach : attachmentList) {
				MimeBodyPart attachBody = new MimeBodyPart();
				attachBody.setDataHandler(new DataHandler(attach.getDataSource()));
				attachBody.setFileName(MimeUtility.encodeText(attach.getName()));
				multipart.addBodyPart(attachBody);
			}
		}
	}

	private void buildContentPart(Multipart multipart, MimeBodyPart htmlBodyPart, MimeBodyPart textBodyPart) throws MessagingException {

		if (textBodyPart != null) {
			multipart.addBodyPart(textBodyPart);
		}
		if (htmlBodyPart != null) {
			multipart.addBodyPart(htmlBodyPart);
		}
	}

	private void buildRelatedBodyPart(Multipart multipart, MimeBodyPart htmlBodyPart, MimeBodyPart textBodyPart) throws MessagingException, IOException {

		MimeBodyPart relatedBodyPart = new MimeBodyPart();
		multipart.addBodyPart(relatedBodyPart);

		MimeMultipart relatedMultipart = new MimeMultipart("related");
		relatedBodyPart.setContent(relatedMultipart);

		if (textBodyPart != null && htmlBodyPart != null) {
			buildAlternativeBodyPart(relatedMultipart, htmlBodyPart, textBodyPart);
		} else {
			buildContentPart(relatedMultipart, htmlBodyPart, textBodyPart);
		}

		for (Attachment attach : embedAttachmentList) {
			MimeBodyPart attachBodyPart = new MimeBodyPart();
			attachBodyPart.setDataHandler(new DataHandler(attach.getDataSource()));
			attachBodyPart.setContentID(attach.getCid());
			attachBodyPart.setFileName(MimeUtility.encodeText(attach.getName()));
			relatedMultipart.addBodyPart(attachBodyPart);
		}
	}

	private void buildAlternativeBodyPart(Multipart multipart, MimeBodyPart htmlBodyPart, MimeBodyPart textBodyPart) throws MessagingException {

		MimeBodyPart alternativeBodyPart = new MimeBodyPart();
		multipart.addBodyPart(alternativeBodyPart);

		MimeMultipart alternativeMultipart = new MimeMultipart("alternative");
		alternativeBodyPart.setContent(alternativeMultipart);

		alternativeMultipart.addBodyPart(textBodyPart);

		alternativeMultipart.addBodyPart(htmlBodyPart);
	}

	private void setBasicInfo() throws MessagingException, UnsupportedEncodingException {

		if (StringUtils.isNotBlank(subject)) {
			message.setSubject(subject);
		}
		message.setSentDate(new Date());
		message.setFrom(from);

		if (!this.headers.isEmpty()) {
			for (String key : this.headers.keySet()) {
				message.setHeader(key, this.headers.get(key));
			}
		}

		if (!recipients.isEmpty()) {
			message.addRecipients(RecipientType.TO, recipients.toArray(new Address[0]));
		}
		if (!ccRecipients.isEmpty()) {
			message.addRecipients(RecipientType.CC, ccRecipients.toArray(new Address[0]));
		}
		if (!BCCRecipients.isEmpty()) {
			message.addRecipients(RecipientType.BCC, BCCRecipients.toArray(new Address[0]));
		}
	}

	public String getSubject() {

		return subject;
	}

	public void setSubject(String subject) {

		this.subject = subject;
	}

	public String getFrom() {

		return from.getAddress();
	}

	public void setFrom(String from) {

		try {
			this.from = new InternetAddress(from);
		} catch (AddressException e) {
		}
	}

	public void setFrom(String from, String person) {

		try {
			this.from = new InternetAddress(from, person, "UTF-8");
		} catch (Exception e) {
		}
	}

	public void setMailer(String mailer) {

		headers.put("X-Mailer", mailer);
	}

	public void setPriority(MailPriority priority) {

		headers.put("X-Priority", priority.value() + "");
	}

	public void setDispositionNotification(boolean need) {

		if (need) {
			this.headers.put("Disposition-Notification-To", "1");
		} else {
			this.headers.remove("Disposition-Notification-To");
		}
	}

	public void setHeader(String key, String value) {

		headers.put(key, value);
	}

	public String getText() {

		return text;
	}

	public void setText(String text) {

		this.text = text;
	}

	public String getHtml() {

		return html;
	}

	public void setHtml(String html) {

		this.html = html;
	}

	public void setSession(Session session) {

		this.session = session;
	}

	public List<Address> getRecipients() {

		return recipients;
	}

	public List<Address> getCCRecipients() {

		return ccRecipients;
	}

	public List<Address> getBCCRecipients() {

		return BCCRecipients;
	}

}
